package oxing.core;

import java.util.concurrent.atomic.AtomicReference;

public abstract class MessagePool {
	public static void handleOnce(MessagePool pool) throws InterruptedException {
		pool.lock();
		try {
			Message m;
			synchronized (pool.thread) {
				while (pool.size == 0)
					pool.thread.wait();
				m = pool.dequeue();
			}
			pool.handler(m);
		} finally {
			pool.unlock();
		}
	}

	public static void handle(MessagePool pool) {
		pool.lock();
		try {
			Message m;
			for (;;) {
				synchronized (pool.thread) {
					if (pool.size > 0)
						m = pool.dequeue();
					else
						return;
				}
				pool.handler(m);
			}
		} finally {
			pool.unlock();
		}
	}

	public static void loopHandle(MessagePool pool) throws InterruptedException {
		pool.lock();
		try {
			Message m;
			for (;;) {
				synchronized (pool.thread) {
					while (pool.size == 0)
						pool.thread.wait();
					m = pool.dequeue();
				}
				pool.handler(m);
			}
		} finally {
			pool.unlock();
		}
	}

	private final AtomicReference<Thread> thread = new AtomicReference<Thread>(null);
	private Message head;
	private Message last;
	private int size;

	protected MessagePool() {
	}

	final void addMessage(Message msg) {
		if (null == msg)
			throw new NullPointerException();
		enqueue(msg);
	}

	private void enqueue(Message msg) {
		synchronized (thread) {
			if (size++ == 0) {
				thread.notify();
				head = last = msg;
			} else {
				last = last.next = msg;
			}
		}
	}

	private Message dequeue() {
		Message m = head;
		if (size > 1) {
			head = m.next;
		} else {
			head = last = null;
			size = 0;
		}
		return m;
	}

	private void handler(Message m) {
		if (m.what < 0)
			return;
		try {
			if (m.getClass() == Message.class) {
				handleMessage(m);
			} else {
				m.run();
			}
			m.recycle();
		} catch (Throwable t) {
			handleMessageException(t);
		}
	}

	protected abstract void handleMessage(Message msg);

	protected void handleMessageException(Throwable throwable) {
	}

	private void lock() {
		Thread t = Thread.currentThread();
		if (thread.compareAndSet(null, t))
			return;
		throw new RuntimeException();
	}

	private void unlock() {
		Thread t = Thread.currentThread();
		if (thread.compareAndSet(t, null))
			return;
		throw new RuntimeException();
	}

	public final boolean equals(Object obj) {
		return obj == this;
	}

	protected final Object clone() throws CloneNotSupportedException {
		return null;
	}

	protected final void finalize() throws Throwable {
	}
}